/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string>

#include "file_io_MAC_LLC.h"
#include "common/FileIO.h"
#include "SceOsMisc.h"


extern "C" {
  FILEIO_MAC_LLC_EXPORT
  POA_CF::Resource *
  CreateFileOutput_MAC_LLC (const char * identifier)
  {
    FileOutput_MAC_LLC * f_out;

    if (FileOutput_MAC_LLC::Create (f_out, identifier) != success) {
      return 0;
    }

    return (POA_CF::Resource *) f_out;
  }
}

static const unsigned int max_errorPackets = 1024;

FileOutput_MAC_LLC::FileOutput_MAC_LLC (const char * identifier)
  : CF__Resource_impl (identifier),
    m_threadrunning (false),
    m_bStop (false)
{
  // Initialize buffers.
  // To be able to handle 4 buffers @ 16 bytes from FM3TR receiver.
  unsigned int input_buffer_size = 176;
  m_input = new char [input_buffer_size];
  m_errorPackets = new short [max_errorPackets];

  m_header = new unsigned char [72];
  mac_buffer_size = 10;

  // Define ports.
  m_filein = new FileInPort_impl (input_buffer_size);
  m_mac_err_ctrl_in = new MAC_ErrCtrl_ReceiveInPort_impl<FileOutput_MAC_LLC> (this);
}

FileOutput_MAC_LLC::~FileOutput_MAC_LLC ()
{
  delete m_filein;
  delete m_mac_err_ctrl_in;

  delete [] m_input;
  delete [] m_header;
  delete [] m_errorPackets;
}

status
FileOutput_MAC_LLC::Create (FileOutput_MAC_LLC *& handle,
                            const char * identifier)
{
  FileOutput_MAC_LLC * Temp = new FileOutput_MAC_LLC (identifier);

  if (!Temp) {
    handle = 0;
    return fail;
  }

  handle = Temp;
  return success;
}

status
FileOutput_MAC_LLC::Init ()
{
#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Initializing." << std::endl;
#endif

  m_header_received = false;
  m_packetCounter = 0;
  m_num_packets = 0;
  m_num_bytes_last_packet = 0;

  m_rewritePacketIndex = 0;

  m_packetFeedbackCounter = 0;
  m_numErrorPackets = 0;
  m_retransmissionMode = false;

  /*
   * Property UUIDs (from .prf.xml).
   *
   * <simple id="DCE:f8e17e88-4347-442b-9841-fd1c3b58757b" type="string" name="Output_File_Name" mode="readwrite">
   *   <value>data_files/data_file_output_test.bin</value>
   *   <kind kindtype="configure" />
   * </simple>
   * <simple id="DCE:b427c15b-8717-43d6-b9d7-ad325dfa9d70" type="string" name="Output_File_Type" mode="readwrite">
   *   <value>octet</value>
   *   <kind kindtype="configure" />
   * </simple>
   */

  int index;

  try {
    {
      const char * prop_name = "DCE:f8e17e88-4347-442b-9841-fd1c3b58757b"; //UUID
      index = GetPropertyIndex (prop_name);

      if (index < 0) {
	return fail;
      }

      if (GetPropertyValue (index, &m_prop_output_file_name)) {
	return fail;
      }
    }

    {
      const char * prop_name = "DCE:b427c15b-8717-43d6-b9d7-ad325dfa9d70"; //UUID
      index = GetPropertyIndex (prop_name);

      if (index < 0) {
	return fail;
      }

      if (GetPropertyValue (index, &m_prop_output_data_type)) {
	return fail;
      }
    }
  }
  catch (...) {
    return fail;
  }

#ifdef MCS_VERBOSE
  std::cout << "FileInput_MAC_LLC: Initializing; output file name \""
	    << m_prop_output_file_name
	    << "\"." << std::endl;
#endif

  if (!(m_file_out = fopen( m_prop_output_file_name, "w+b"))) {
#ifdef MCS_VERBOSE
    std::cout << "FileOutput_MAC_LLC: Failed to open output file \""
	      << m_prop_output_file_name
	      << "\"." << std::endl;
#endif
    return fail;
  }

  m_data_type = ConvertStringToDataTypeEnum (m_prop_output_data_type);
  return success;
}

// Generic thread OS signature which calls component specific thread function.
void
FileOutput_MAC_LLC::RunWorker (void * param)
{
  FileOutput_MAC_LLC * component =
    (FileOutput_MAC_LLC *) param;

#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Component is running." << std::endl;
#endif

  component->RunLoop ();

#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Component is stopping." << std::endl;
#endif
}

// Inner loop function that checks for a stop command.
status
FileOutput_MAC_LLC::RunLoop ()
{
  m_bStop = false;

  if (!Init ()) {
    return fail;
  }

  while (!m_bStop) {
    if (!Run ()) {
      return fail;
    }
  }

  // Close output file if open.

  if (m_file_out) {
    fclose (m_file_out);
  }

  m_file_out = 0;
  m_threadrunning = false;
  m_thread.detach ();
  return success;
}

void
FileOutput_MAC_LLC::start()
  throw (CORBA::SystemException, CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start ();
}

void
FileOutput_MAC_LLC::stop ()
  throw (CORBA::SystemException, CF::Resource::StopError)
{
  m_bStop = true;
}

void
FileOutput_MAC_LLC::signalACK ()
{
#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Received ACK." << std::endl;
#endif

  m_packetFeedbackCounter++;
}

void
FileOutput_MAC_LLC::signalNAK ()
{
  std::cout << "FileOutput_MAC_LLC: Received NAK." << std::endl;

  int packetID = m_packetFeedbackCounter;
  m_packetFeedbackCounter++;

  // A dummy packet is sent if the total number of packets is even.
  if ((packetID==(m_num_packets+1)) && ((m_num_packets%2)==0)) {
#ifdef MCS_VERBOSE
    std::cout << "FileOutput_MAC_LLC: Ignoring dummy packet." << std::endl;
#endif
    return;
  }

  if (!m_retransmissionMode) {
    assert (packetID <= 32767);
    assert (m_numErrorPackets < max_errorPackets);
    m_errorPackets[m_numErrorPackets] = static_cast<short> (packetID);
    m_numErrorPackets++;
  }
  else {
    // Retransmission mode:
    //   m_packetFeedbackCounter has been reset.
    //   Ignore every other packet (dummy packet).

    if ((m_packetFeedbackCounter%2)==0) {
#ifdef MCS_VERBOSE
      std::cout << "FileOutput_MAC_LLC: Ignoring dummy packet." << std::endl;
#endif
      return;
    }

    // Set packet ID to equivalent FileInput block index.
    packetID = ((packetID-m_num_packets) - (1-(m_num_packets%2)))/2;
    assert (m_numErrorPackets < max_errorPackets);
    m_errorPackets[m_numErrorPackets] = m_errorPackets[packetID];
    m_numErrorPackets++;
  }

#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Erroneous packets: ";
  for (int jj=0; jj<m_numErrorPackets; jj++) {
    if (jj) {
      std::cout << ", ";
    }
    std::cout << m_errorPackets[jj];
  }
  std::cout << "." << std::endl;
#endif
}

status
FileOutput_MAC_LLC::RewritePacket (short packetID)
{
  // Write data to file.

  if (!m_file_out) {
    return fail;
  }

  size_t data_size = GetSizeOfDataType(m_data_type);
  short num_samples = (packetID==m_num_packets) ? m_num_bytes_last_packet : 72;

  // Parse the file for packetID-1 blocks.
  int result = fseek( m_file_out, (packetID-1)*72, SEEK_SET);

  if (result) {
    // fseek failed!
    return fail;
  }

  int write_status = fwrite (m_input, data_size, num_samples, m_file_out);

  if (write_status != num_samples) {
    // fwrite failed!
    return fail;
  }

  return success;
}

status
FileOutput_MAC_LLC::ExtractHeader ()
{
  assert (m_input_count >= 72);

  // Header: unpack 36 bytes @ 8 bits from 72 bytes @ 4 bits.
  for (int i=0; i<36; i++) {
    m_header[i] = (((m_input[2*i]&0x0f)<<4) | (m_input[2*i+1]&0x0f));
  }

  unsigned char * header_ptr = m_header;

  // Calling platform address.
  int sourceAddress = (int) (*header_ptr++);

  // Called platform address
  int destAddress = (int) (*header_ptr++);

  // Message ID.
  header_ptr += 11;

  // Message length.
  short *header_short_ptr = (short *)header_ptr;
  m_num_packets = *header_short_ptr;
  m_num_bytes_last_packet = *(header_short_ptr+1);

#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Source address: " << sourceAddress << std::endl
	    << "                       Destination: " << destAddress << std::endl
	    << "                 Number of Packets: " << m_num_packets << std::endl
	    << "              Bytes in last Packet: " << m_num_bytes_last_packet << std::endl;
#endif

  return success;
}

CORBA::Object_ptr
FileOutput_MAC_LLC::getPort (const CORBA::Char * name)
  throw (CORBA::SystemException, CF::PortSupplier::UnknownPort)
{
  std::string temp (name);

  if (temp == "FileOutput_MAC_LLC_In") {
    return m_filein->getPort ();
  }
  else if (temp == "FileOutput_MAC_LLC_ControlIn") {
    return m_mac_err_ctrl_in->getPort ();
  }
  else {
    throw CF::PortSupplier::UnknownPort ();
  }
}

// Inner loop method that performs component business logic.
status
FileOutput_MAC_LLC::Run ()
{
  size_t data_size, status;

  m_input_count = m_filein->GetData (&m_input);

  if (m_input_count == 0) {
    // data not ready yet
    return success;
  }
  else if (m_input_count < 72) {
    // Not enough samples in input buffer.
    m_filein->ReleaseDataBuffer (0);
    return success;
  }

#ifdef MCS_VERBOSE
  std::cout << "FileOutput_MAC_LLC: Received "
	    << m_input_count
	    << " bytes of data."
	    << std::endl;
#endif

  // Extract the header (first packet) if not already done.
  if (!m_header_received) {
    if (ExtractHeader () != success) {
      return fail;
    }

    m_filein->ReleaseDataBuffer (72);
    m_header_received = true;
    return success;
  }

  data_size = GetSizeOfDataType (m_data_type);

  m_packetCounter++;

  if (m_packetCounter < m_num_packets) {
    // Normal write mode.
    status = fwrite (m_input, data_size, 72, m_file_out);
    m_total_output_count += status;
  }
  else if (m_packetCounter == m_num_packets) {
    // Last packet.
    status = fwrite(m_input, data_size, m_num_bytes_last_packet, m_file_out);
    m_total_output_count += m_num_bytes_last_packet;
#ifdef MCS_VERBOSE
    std::cout << "FileOutput_MAC_LLC: At EOF after "
	      << m_packetCounter
	      << " packets."
	      << std::endl;
#endif
    m_retransmissionMode = true;
  }
  else {
    // Retransmission mode
    // 
    // Check to see if it is ok to write packet (dummy, etc.)
    // Dummy packets are sent at the end of the initial sequence
    //   if the total number of packets is even.  They are also
    //   sent after every packet retransmission (m_packetCounter is
    //   odd).

    if (((m_packetCounter%2)==1) ||
	((m_packetCounter==m_num_packets+1) && (m_num_packets%2==0))) {
      // Ignore this dummy packet
#ifdef MCS_VERBOSE
      std::cout << "FileOutput_MAC_LLC: Ignoring dummy packet." << std::endl;
#endif
    }
    else {
#ifdef MCS_VERBOSE
      std::cout << "FileOutput_MAC_LLC: Retransmisstion of packet " 
		<< m_errorPackets[m_rewritePacketIndex]
		<< "." << std::endl;
#endif
      RewritePacket (m_errorPackets[m_rewritePacketIndex]);
      m_rewritePacketIndex++;
    }
  }

  m_filein->ReleaseDataBuffer (72);
  m_input_count -= 72;
  return success;
}
